﻿using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.Windows.Controls;

using PasswordProvider.ObjectModel;
using PasswordProvider.ViewModel.Properties;
using PasswordProvider.ViewModel.Helpers;
using PasswordProvider.ViewModel.Framework;

namespace PasswordProvider.ViewModel
{
    /// <summary>
    /// Base class for site details view. Provides editing, data validation and data transfer of Site 
    /// properties Guid, Name, Description, Url and Tags. 
    /// Provides the commands CommitChangesCommand and CancelCommand.
    /// </summary>
    public abstract class SiteDetailsViewModel : VisualViewModel, IRequestClose, INotifyChangesCommited
    {

        #region Construction

        private bool _isNewSite;

        internal SiteDetailsViewModel(Site site, bool isNew)
            : base()
        {
            _guid = site.Guid;
            Site = site;
            _isNewSite = isNew;
        }

        #endregion // Construction
        
        #region INotifyChangesCommited Members

        public event EventHandler ChangesCommitted;

        #endregion

        #region ICompleteTask Members

        public event EventHandler CloseRequested;

        #endregion

       
        #region Public and Internal Properties

        internal Site Site
        {
            get;
            private set;
        }


        /// <summary>
        /// The site's Guid.
        /// </summary>
        public Guid SiteGuid
        {
            get { return _guid; }
        } private Guid _guid;

        public TextEditViewModel Name
        {
            get
            {
                if (_name == null)
                {
                    _name = new TextEditViewModel(
                        Strings.SiteDetails_Name_DisplayName,
                        Strings.SiteDetails_Name_ToolTip,
                        Site.Name);
                    _name.PropertyChanged += delegate { OnPropertyChanged("Name"); };
                }
                return _name;
            }
        } private TextEditViewModel _name;


        /// <summary>
        /// The site's description
        /// </summary>
        public TextEditViewModel Description
        {
            get
            {
                if (_description == null)
                {
                    _description = new TextEditViewModel(
                        Strings.SiteDetails_Description_DisplayName,
                        Strings.SiteDetails_Description_ToolTip,
                        Site.Description);
                    _description.PropertyChanged += delegate { OnPropertyChanged("Description"); };
                }
                return _description;
            }
        } private TextEditViewModel _description;

        
        /// <summary>
        /// The site's tags.
        /// </summary>
        public ListSelectViewModel<string> Tags
        {
            get
            {
                if (_tags == null)
                {
                    _tags = new ListSelectViewModel<string>(
                        Strings.SiteDetails_Tags_DisplayName,
                        Strings.SiteDetails_Tags_ToolTip,
                        GetGloballyUniqueTags(),
                        Site.Tags);
                    _tags.PropertyChanged += delegate { OnPropertyChanged("Tags"); };
                }
                return _tags;
            }
        } private ListSelectViewModel<string> _tags;


        /// <summary>
        /// CommitChangesCommand: Invoke when all changes are to be committed to the data store. 
        /// Ignored if no changes are made.
        /// </summary>
        public CommandViewModel CommitChangesCommand
        {
            get
            {
                if (_commitChangesCommand == null)
                {
                    _commitChangesCommand = new CommandViewModel(
                        Strings.SiteDetails_CommitChanges_DisplayName,
                        Strings.SiteDetails_CommitChanges_ToolTip,
                        _isNewSite,
                        new RelayCommand(
                            param => CommitChangesCommandExecuted(),
                            param => CommitChangesCommandCanExecute()));
                }
                return _commitChangesCommand;
            }
        } private CommandViewModel _commitChangesCommand;

        /// <summary>
        /// CancelCommand: Invoke when all changes are to be ignored and the view should be closed.
        /// </summary>
        public CommandViewModel CancelCommand
        {
            get
            {
                if (_cancelCommand == null)
                {
                    _cancelCommand = new CommandViewModel(
                        Strings.SiteDetails_Cancel_DisplayName,
                        Strings.SiteDetails_Cancel_ToolTip,
                        !_isNewSite,
                        new RelayCommand(param => CancelCommandExecuted()));
                }
                return _cancelCommand;
            }

        } private CommandViewModel _cancelCommand;

        #endregion // Pulic Interface

        #region Protected methods

        /// <summary>
        /// Abstract method to transfer properties from the view model to the underlying Site.
        /// </summary>
        protected abstract void TransferProperties();

        /// <summary>
        /// Transfers local property values to the site. 
        /// </summary>
        private void TransferPropertiesBase()
        {
            Site.Name = Name.Text;
            Site.Description = Description.Text;

            Site.Tags.Clear();
            foreach(string tag in Tags.SelectedItems)
                Site.Tags.Add(tag);

            TransferProperties();

            Site.Commit();
        }

        #endregion

        #region Command Handlers

        /// <summary>
        /// CommitChangesCommandExecuted
        /// </summary>
        private void CommitChangesCommandExecuted()
        {
            if (IsChanged)
            {
                TransferPropertiesBase();
            }

            if(ChangesCommitted != null)
                ChangesCommitted(this, new EventArgs());
            
            if(CloseRequested != null)
                CloseRequested(this, new EventArgs());
        }
        /// <summary>
        /// CommitChangesCommandCanExecute
        /// </summary>
        /// <returns></returns>
        private bool CommitChangesCommandCanExecute()
        {
            return IsChanged;
        }
        /// <summary>
        /// CancelCommandExecuted
        /// </summary>
        private void CancelCommandExecuted()
        {
            // It is an error if this event is not being handled.
            CloseRequested(this, new EventArgs());
        }


        #endregion

        #region Private Helpers

        /// <summary>
        /// Generates the list of unique tags across all sites, for the drop down.
        /// </summary>
        private static ObservableCollection<string> GetGloballyUniqueTags()
        {
            // My first linq statement! The first part returns the set of lists of tags from each site
            // .SelectMany flattens the results into a single array, .Distinct removes duplicates from the list 
            var tags = (from t in PasswordProviderData.Current.Sites select t.Tags)
                .SelectMany(n => n).Distinct();

            ObservableCollection<string> col = new ObservableCollection<string>(tags);
            return col;
            
        }

        #endregion // Private Helpers.

    }
}
